import copy

from PyQt5.Qt import *
import json
from controllers.MindMapEdge import Edge
from models.custom_item import AddRemoveCommand
from models.node_model import *
from sub_window.node_form import NodeFormWindow
from PyQt5 import QtCore, QtGui
from controllers.MindMapEdge import Edge
from PyQt5.QtGui import QWheelEvent


class CustomGraphicsview(QGraphicsView):
    def __init__(self, parent=None):
        super(CustomGraphicsview, self).__init__(parent)
        self.setRenderHints(self.renderHints() | Qt.SmoothTransformation) # 设置渲染模式为平滑模式
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)  # 设置缩放模式为鼠标下方
        # self.setDragMode(QGraphicsView.ScrollHandDrag)  # 设置拖拽模式为滚轮模式
        self.setDragMode(QGraphicsView.NoDrag)  # 设置拖拽模式为禁止模式
        self.zoom = 0

    def wheelEvent(self, event: QWheelEvent):
        factor = 1.1
        if event.angleDelta().y() > 0:
            self.zoom += 1
            self.scale(factor, factor)
        else:
            self.zoom -= 1
            self.scale(1 / factor, 1 / factor)
        if self.zoom > 10:
            self.zoom = 10
            return
        if self.zoom < -10:
            self.zoom = -10
            return


    def keyPressEvent(self, event: QKeyEvent):
        if event.key() == Qt.Key_Control:
            self.setDragMode(QGraphicsView.ScrollHandDrag)  # 当按下Ctrl时，设置拖拽模式为滚轮模式
        super(CustomGraphicsview, self).keyPressEvent(event)

    def keyReleaseEvent(self, event: QKeyEvent):
        if event.key() == Qt.Key_Control:
            self.setDragMode(QGraphicsView.NoDrag)  # 当释放Ctrl后，设置拖拽模式为禁止模式
        # super(CustomGraphicsView, self).keyReleaseEvent(event)


# 处理鼠标事件
class CustomGraphicsScene(QGraphicsScene):
    def __init__(self, parent=None):
        super(CustomGraphicsScene, self).__init__(parent)
        self.mouse_press_pos = None
        self.copy_top_node_pos = QPointF(0, 0)  # copy节点里最上面的节点位置
        self.current_edge = None
        self.start_node = None
        self.mode = 'drag'  # 默认拖动模式
        self.parent = parent
        self.rubberBand = None
        self.rubberBandRect = QRect()
        self.nodes_draggable = True
        self.draw_grid()
        self.context_menu_pos = None
        self.clipboard = QApplication.clipboard()

        self.undo_stack = QUndoStack(self)

    def set_mode(self, mode):  # 设置拖拽/连线模式
        self.mode = mode

    def mousePressEvent(self, event):
        print("scene press event")
        scene_pos = event.scenePos()
        self.mouse_press_pos = scene_pos
        # 绘制选中区域（橡皮筋）
        click_node = False
        for item in self.items(scene_pos):
            if isinstance(item, (SmallRectNode, SubNode, RootNode, TextNode)):
                click_node = True
                break
        if event.button() == Qt.LeftButton and event.modifiers() != Qt.ControlModifier and not click_node:  # ctrl未按下时，左键按下，且未选中节点
            print(f'Press[{event.scenePos()}]')
            self.rubberBandRect.setTopLeft(event.scenePos().toPoint())
            self.rubberBandRect.setBottomRight(event.scenePos().toPoint())
            self.rubberBand = QGraphicsRectItem(QRectF(self.rubberBandRect))
            self.rubberBand.setPen(QPen(Qt.black, 0.5, Qt.DashLine))
            self.addItem(self.rubberBand)
        super(CustomGraphicsScene, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        # 绘制选中区域（橡皮筋）
        if self.rubberBand:
            self.rubberBandRect.setBottomRight(event.scenePos().toPoint())
            rubberBandRect = QRect()
            rubberBandRect.setTopLeft(self.rubberBandRect.topLeft())
            rubberBandRect.setBottomRight(self.rubberBandRect.bottomRight())
            if self.rubberBandRect.width() < 0:
                rubberBandRect.setX(self.rubberBandRect.x() + self.rubberBandRect.width())
                rubberBandRect.setWidth(-self.rubberBandRect.width())
            if self.rubberBandRect.height() < 0:
                rubberBandRect.setY(self.rubberBandRect.y() + self.rubberBandRect.height())
                rubberBandRect.setHeight(-self.rubberBandRect.height())
            self.rubberBand.setRect(QRectF(rubberBandRect))
        super(CustomGraphicsScene, self).mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        # 绘制选中区域（橡皮筋）
        if event.button() == Qt.LeftButton:
            selected_items = []
            for item in self.items():
                if self.rubberBandRect.contains(item.sceneBoundingRect().toRect()):
                    selected_items.append(item)

            # Clear the rubber band
            self.removeItem(self.rubberBand)
            self.rubberBand = None
            self.rubberBandRect = QRect()

            # Select the items
            for item in selected_items:
                item.setSelected(True)
        super(CustomGraphicsScene, self).mouseReleaseEvent(event)

    def mouseDoubleClickEvent(self, event):
        print("Double Click on scene")
        super(CustomGraphicsScene, self).mouseDoubleClickEvent(event)

    def contextMenuEvent(self, event):
        pos_temp = event.scenePos()
        self.context_menu_pos = QPointF(pos_temp.x(), pos_temp.y())
        context_menu = QMenu(self.parent)

        add_rect_action = QAction('添加子节点', self)
        add_rect_action.triggered.connect(self.add_sub_node)
        context_menu.addAction(add_rect_action)

        add_ellipse_action = QAction('添加根节点', self)
        add_ellipse_action.triggered.connect(self.add_root_node)
        context_menu.addAction(add_ellipse_action)

        add_text_action = QAction('添加文本节点', self)
        add_text_action.triggered.connect(self.add_text_node)
        context_menu.addAction(add_text_action)

        charge_mode_action = QAction('复制选中节点', self)
        charge_mode_action.triggered.connect(self.copy_nodes)
        context_menu.addAction(charge_mode_action)

        load_action = QAction('粘贴', self)
        load_action.triggered.connect(self.paste_nodes)
        context_menu.addAction(load_action)

        save_action = QAction('保存', self)
        save_action.triggered.connect(self.save_nodes)
        context_menu.addAction(save_action)
        context_menu.exec_(event.screenPos())

        super(CustomGraphicsScene, self).contextMenuEvent(event)

    def keyPressEvent(self, event: QKeyEvent):
        if event.key() == Qt.Key_Delete:
            self.delete_selected_nodes()
        elif event.key() == Qt.Key_Z:
            self.undo()
        elif event.key() == Qt.Key_C:
            self.copy_nodes()
        elif event.key() == Qt.Key_V:
            self.paste_nodes()
        else:
            super().keyPressEvent(event)
        super(CustomGraphicsScene, self).keyPressEvent(event)

    def add_sub_node(self):
        node = SubNode(0, 0, 100, 50, self)
        node.setPos(self.context_menu_pos)
        self.addItem(node)
        self.add_item_with_undo(node)

    def add_root_node(self):
        node = RootNode(0, 0, 100, 50, self)
        node.setPos(self.context_menu_pos)
        self.addItem(node)
        self.add_item_with_undo(node)

    def add_text_node(self):
        node = TextNode('Text Node')
        node.setPos(self.context_menu_pos)
        self.addItem(node)
        self.add_item_with_undo(node)

    def delete_selected_nodes(self):
        selected_items = self.selectedItems()
        self.remove_item_with_undo([item for item in selected_items if isinstance(item, (RootNode, SubNode))])
        for item in selected_items:
            if isinstance(item, (RootNode, SubNode)):
                for edge in item.edges:
                    self.removeItem(edge)
                # 在当前节点的子节点中的父节点列表中删除当前节点
                for node in item.child_node:
                    node.parent_node.remove(item)
                # 在当前节点的父节点中的子节点列表中删除当前节点
                for node in item.parent_node:
                    node.child_node.remove(item)

                self.removeItem(item)
            if isinstance(item, TextNode):
                self.removeItem(item)

    def copy_nodes(self):
        # 复制选中节点
        selected_items = self.selectedItems()
        copied_data = {
            "nodes": [],
            "edges": []
        }
        bCopyFirstNode = True
        for item in selected_items:
            if isinstance(item, (RootNode, SubNode)):
                node_data = {
                    'type': type(item).__name__,
                    'pos': [item.scenePos().x(), item.scenePos().y()],
                    'text': item.text.toPlainText(),
                    'size': [item.rect().width(), item.rect().height()],
                    'id': self.items().index(item)
                }
                copied_data['nodes'].append(node_data)
                if bCopyFirstNode:
                    self.copy_top_node_pos = item.scenePos()
                    bCopyFirstNode = False
                if (self.copy_top_node_pos.y() > item.scenePos().y() or
                        (self.copy_top_node_pos.y() == item.scenePos().y() and self.copy_top_node_pos.x() > item.scenePos().x())):
                    self.copy_top_node_pos = item.scenePos()
            elif isinstance(item, Edge):
                edge_data = {
                    'start_node': self.items().index(item.start_node),
                    'end_node': self.items().index(item.dest_node),
                }
                copied_data['edges'].append(edge_data)
            elif isinstance(item, TextNode):
                data = {
                    'type': 'TextNode',
                    'text': item.toPlainText(),
                    'pos': [item.pos().x(), item.pos().y()],
                    'color': item.defaultTextColor().name()
                }
        # 将数据放入剪贴板
        self.clipboard.setText(json.dumps(copied_data))
        # 取消选中状态
        for item in selected_items:
            item.setSelected(False)

        # 复制完成后初始化左键点击获取到的位置信息
        self.mouse_press_pos = None

    def paste_nodes(self):
        # 粘贴节点
        try:
            copied_data = json.loads(self.clipboard.text())
        except json.JSONDecodeError:
            print('Clipboard data is not a valid JSON file')
            return
        pasted_items = []

        nodes_data = copied_data['nodes']
        edges_data = copied_data['edges']
        nodes = {}
        if self.context_menu_pos is None:
            if self.mouse_press_pos is None:
                offset = QPointF(20, 20)
            else:
                offset = self.mouse_press_pos - self.copy_top_node_pos
        else:
            offset = self.context_menu_pos - self.copy_top_node_pos
        for node_data in nodes_data:
            if node_data['type'] == 'RootNode':
                node = RootNode(0, 0, node_data['size'][0], node_data['size'][1],
                                self, node_data.get("text"))
            elif node_data['type'] == 'SubNode':
                node = SubNode(0, 0, node_data['size'][0], node_data['size'][1],
                               self, node_data.get("text"))
            elif node_data['type'] == 'TextNode':
                node = TextNode(node_data['text'])
            node.setPos(node_data['pos'][0] + offset.x(), node_data['pos'][1] + offset.y())
            self.addItem(node)
            nodes[node_data['id']] = node
            pasted_items.append(node)

        for edge_data in edges_data:
            start_node_id = edge_data['start_node']
            end_node_id = edge_data['end_node']
            if nodes.get(end_node_id):
                edge = Edge(nodes[start_node_id], nodes[end_node_id])
                edge.track_node()
                self.addItem(edge)
                pasted_items.append(node)

        # 选中粘贴的节点
        for pasted_item in pasted_items:
            pasted_item.setSelected(True)

        # 粘貼完成后初始化右鍵和左键点击获取到的位置信息
        self.context_menu_pos = None
        self.mouse_press_pos = None

    def toggle_mode(self):
        if self.mode == 'drag':
            self.set_mode('connect')
            self.parent.graphicsView.viewport().setProperty("cursor",
                                                            QtGui.QCursor(QtCore.Qt.CrossCursor))  # 更改鼠标图标为十字架
        else:
            self.set_mode('drag')
            self.parent.graphicsView.viewport().setProperty("cursor", QtGui.QCursor(QtCore.Qt.ArrowCursor))  # 更改鼠标图标为箭头
        self.nodes_draggable = not self.nodes_draggable
        self.set_nodes_draggable(self.nodes_draggable)

    def load_nodes(self, file_path=""):
        self.clear_all_nodes()  # 清除原有节点, 重新加载
        try:
            with open(file_path, 'r') as f:
                data = json.load(f)
            nodes_data = data['nodes']
            edges_data = data['edges']
            nodes = {}
            for node_data in nodes_data:
                if node_data['type'] == 'RootNode':
                    node = RootNode(0, 0, node_data['size'][0], node_data['size'][1],
                                    self, node_data.get("text"))
                elif node_data['type'] == 'SubNode':
                    node = SubNode(0, 0, node_data['size'][0], node_data['size'][1],
                                   self, node_data.get("text"))
                elif node_data['type'] == 'TextNode':
                    node = TextNode(node_data['text'])
                node.setPos(node_data['pos'][0], node_data['pos'][1])
                self.addItem(node)
                nodes[node_data['id']] = node

            for edge_data in edges_data:
                start_node_id = edge_data['start_node']
                end_node_id = edge_data['end_node']
                edge = Edge(nodes[start_node_id], nodes[end_node_id])
                edge.track_node()
                self.addItem(edge)

            print('Nodes and edges loaded from nodes.json')
        except FileNotFoundError:
            print('nodes.json not found')
        except json.JSONDecodeError:
            print('nodes.json is not a valid JSON file')
        except KeyError:
            print('nodes.json is missing required fields')
        except Exception as e:
            print(f'Error loading nodes: {e}')

    def save_nodes(self):
        nodes = []
        edges = []
        for item in self.items():
            if isinstance(item, (RootNode, SubNode)):
                node_data = {
                    'type': type(item).__name__,
                    'pos': [item.scenePos().x(), item.scenePos().y()],
                    'text': item.text.toPlainText(),
                    'size': [item.rect().width(), item.rect().height()],
                    'id': self.items().index(item)
                }
                nodes.append(node_data)
            elif isinstance(item, Edge):
                edge_data = {
                    'start_node': self.items().index(item.start_node),
                    'end_node': self.items().index(item.dest_node),
                }
                edges.append(edge_data)

        data = {
            'nodes': nodes,
            'edges': edges,
        }
        current_index = self.parent.treeView_ToolBar.currentIndex()
        current_path = self.parent.tree_view_manager.model.filePath(current_index)
        with open(current_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=4)

    def clear_all_nodes(self):
        # TODO: 检查当前文件是否需要保存, 并提示用户保存
        selected_items = self.items()
        for item in selected_items:
            if isinstance(item, (RootNode, SubNode, SmallRectNode)):
                for edge in item.edges:
                    self.removeItem(edge)
                self.removeItem(item)
            if isinstance(item, TextNode):
                self.removeItem(item)
        # Clear the nodes dictionary
        self.nodes = {}

    def set_nodes_draggable(self, draggable):
        for item in self.items():
            if isinstance(item, (RootNode, SubNode, SmallRectNode)):
                item.setFlag(QGraphicsItem.ItemIsMovable, draggable)

    def draw_grid(self):
        # 绘制网格
        self.clear()  # 清除原有图形

        # 颜色和线宽
        pen_axis = Qt.black  # 坐标轴颜色
        pen_grid = Qt.lightGray  # 网格颜色

        # 绘制X轴\Y轴
        self.addLine(-10000, 0, 10000, 0, pen_axis)
        self.addLine(0, -10000, 0, 10000, pen_axis)

        # 绘制网格
        step = 50  # 网格间隔
        for x in range(-10000, 10000, step):
            if x == 0:
                continue
            self.addLine(x, -10000, x, 10000, pen_grid)
            # Add coordinate text
            coordinate_text = QGraphicsTextItem(f'{x}')
            coordinate_text.setDefaultTextColor(Qt.blue)
            coordinate_text.setPos(x, 0)
            coordinate_text.setFont(QFont('Arial', 5))  # Adjust font size here
            self.addItem(coordinate_text)
        for y in range(-10000, 10000, step):
            if y == 0:
                continue
            self.addLine(-10000, y, 10000, y, pen_grid)
            # Add coordinate text
            coordinate_text = QGraphicsTextItem(f'{y}')
            coordinate_text.setDefaultTextColor(Qt.blue)
            coordinate_text.setPos(0, y)
            coordinate_text.setFont(QFont('Arial', 5))  # Adjust font size here
            self.addItem(coordinate_text)

        # 在原点绘制一个圆
        radius = 2
        origin_circle = QGraphicsEllipseItem(-radius, -radius, 2 * radius, 2 * radius)
        origin_circle.setBrush(QBrush(Qt.white))
        origin_circle.setPen(QPen(Qt.black, 0.5))
        self.addItem(origin_circle)

        # 绘制原点坐标
        origin_text = QGraphicsTextItem('(0, 0)')
        origin_text.setDefaultTextColor(Qt.blue)
        origin_text.setPos(0, 0)
        origin_text.setFont(QFont('Arial', 5))  # Adjust font size here
        self.addItem(origin_text)

    def add_item_with_undo(self, item):
        command = AddRemoveCommand(self, item, True)
        self.undo_stack.push(command)

    def remove_item_with_undo(self, item):
        command = AddRemoveCommand(self, item, False)
        self.undo_stack.push(command)

    def undo(self):
        self.undo_stack.undo()

    def redo(self):
        self.undo_stack.redo()
